//! Model Layer
//!
//! Design:
//!
//! - The Model layer normalizes the application's data type
//!   structures and access.
//! - All application code data access must go through the Model layer.
//! - The `ModelManager` holds the internal states/resources
//!   needed by ModelControllers to access data.
//!   (e.g., db_pool, S3 client, redis client).
//! - Model Controllers (e.g., `TaskBmc`, `ProjectBmc`) implement
//!   CRUD and other data access methods on a given "entity"
//!   (e.g., `Task`, `Project`).
//!   (`Bmc` is short for Backend Model Controller).
//! - In frameworks like Axum, Tauri, `ModelManager` are typically used as App State.
//! - ModelManager are designed to be passed as an argument
//!   to all Model Controllers functions.
//!

// region:    --- Modules

mod base;
mod error;
mod store;
pub mod task;
pub mod user;

use std::str::FromStr;

use tracing::{debug, info};

pub use self::error::{Error, Result};
use crate::model::store::{new_db_pool, Db};
// endregion: --- Modules

#[derive(Clone)]
pub struct ModelManager {
    db: Db,
}

impl ModelManager {
    pub async fn new(db_url: &str) -> Result<Self> {
        let db = new_db_pool(db_url).await?;

        // let peer: std::result::Result<PeerId,libp2p_core::peer_id::ParseError> = PeerId::from_str("QmRSGx67Kq8w7xSBDia7hQfbfuvauMQGgxcwSWw976x4BS");
        
        // let addr: std::result::Result<Multiaddr, ipfs_embed::multiaddr::Error> = Multiaddr::from_str("/ip4/54.173.33.96/tcp/4001");
        // ipfs.dial_address(peer.unwrap(), addr.unwrap());

        // //  For example, the peer ID and address of one of the Cloudflare nodes are:
        // let peer: PeerId = "QmcFf2FH3CEgTNHeMRGhN7HNHU1EXAxoEk6EFuSyXCsvRE".parse()?;
        // let addr: Multiaddr = "/dnsaddr/node-1.ingress.cloudflare-ipfs.com".parse()?;

        Ok(ModelManager { db  })
    }

    /// Returns the sqlx db pool reference.
    /// (Only for the model layer)
    pub(in crate::model) fn db(&self) -> &Db {
        &self.db
    }


    pub async fn exec(&self, sql: &str) -> Result<()> {
        let rs = sqlx::query(sql).execute(&self.db).await;

        debug!("[ModelManager] exec rs={:?}", rs);
        Ok(())
    }
}
